#from khronos.tkwidgets import quick_app, FullMenu, ProgressBar, StatusBar, ToolTip 

from Tkinter import *
from math import ceil
from time import time

from nipponsteel.utils import INF


def quick_app(title=None):
    app = Tk()
    if title is not None:
        app.title(title)
    app.rowconfigure(0, weight=1)
    app.columnconfigure(0, weight=1)
    return app
    
    
class Rect(object):
    def __init__(self, *points):
        self.x0 = +INF
        self.x1 = -INF
        self.y0 = +INF
        self.y1 = -INF
        for x, y in points:
            self.extend_to(x, y)
            
    def __iter__(self):
        return iter([self.x0, self.y0, self.x1, self.y1])
        
    @property
    def width(self):
        return self.x1 - self.x0
        
    @property
    def height(self):
        return self.y1 - self.y0
        
    @property
    def center(self):
        return float(self.x0 + self.x1) / 2.0, float(self.y0 + self.y1) / 2.0
        
    def extend_to(self, x, y):
        if x < self.x0:
            self.x0 = x
        if x > self.x1:
            self.x1 = x
        if y < self.y0:
            self.y0 = y
        if y > self.y1:
            self.y1 = y
            
    def inflate(self, ratio):
        x, y = self.center
        w, h = self.width * ratio, self.height * ratio
        self.x0 = x - w / 2.0
        self.x1 = x + w / 2.0
        self.y0 = y - h / 2.0
        self.y1 = y + h / 2.0
        
    def copy(self):
        return Rect((self.x0, self.y0), (self.x1, self.y1))
         
         
class FullMenu(Menubutton):
    """This is a regular menu button, but it automatically creates and links a menu to the button.
    This is useful because it is needed all the time, so it simplifies the creation of menus.
    """
    def __init__(self, master, name, tearoff=False):
        Menubutton.__init__(self, master, text=name)
        self.menu = Menu(self, tearoff=tearoff)
        self.configure(menu=self.menu)
        
        
class ProgressBar(Canvas):
    def __init__(self, master, width=1200, height=20, command=None, user_setable=True):
        Canvas.__init__(self, master, width=width, height=height, bg="grey")
        self.widget = width
        self.height = height
        self.command = command
        self.value = 0.0
        self.user_setable = user_setable
        self.setup()
        
    def setup(self):
        # setup an action when the canvas is clicked
        self.bind("<ButtonRelease-1>", self.user_set)
        self.bind("<Configure>", self.update_size)
        self.draw()
        
    def draw(self):
        # draw the bar
        pos = self.value * self.widget
        self.create_rectangle(-self.widget-10 + pos, 0, pos, self.height, 
                              fill="green", outline="green", tags=("bar",))
        # draw vertical lines every 10%
        tenth = self.widget / 10.0
        for x in xrange(1, 10):
            x = ceil(x * tenth)
            self.create_line(x, 0, x, self.height, fill="white", tags=("marker",))
        # draw the percentage text
        self.create_text(0.5 * self.widget, 0.5 * self.height, 
                         text="%.2f%%" % (self.value * 100,), tags=("progress",))
        
    def clear(self):
        self.delete("bar")
        self.delete("marker")
        self.delete("progress")
        
    def update_size(self, event=None):
        self.clear()
        self.widget = self.winfo_width()
        self.height = self.winfo_height()
        self.draw()
        
    def set(self, value):
        assert 0.0 <= value <= 1.0
        old_pos = self.value * self.widget
        new_pos = value * self.widget
        self.move("bar", new_pos - old_pos, 0)
        self.itemconfigure("progress", text="%.2f%%" % (value * 100,))
        self.value = value
        
    def user_set(self, event):
        """Called when a user clicked the progress bar, so command must be invoked."""
        if self.user_setable:
            value = max(0.0, min(1.0, float(event.x) / self.widget))
            self.set(value)
            if self.command is not None:
                self.command(value)
                
                
class StatusBar(Frame):
    def __init__(self, master, info="", status="", info_weight=5, status_weight=1):
        Frame.__init__(self, master)
        self.columnconfigure(0, weight=info_weight)
        self.columnconfigure(1, weight=status_weight)
        self.info = StringVar(value=info)
        self.status = StringVar(value=status)
        info_label = Label(self, relief=GROOVE, textvariable=self.info, anchor=W)
        status_label = Label(self, relief=GROOVE, textvariable=self.status, anchor=E)
        info_label.grid(row=0, column=0, sticky=NE+SW)
        status_label.grid(row=0, column=1, sticky=NE+SW)
        
        
class Tooltip(Toplevel):
    """
    Provides a ToolTip widget for Tkinter.
    To apply a ToolTip to any Tkinter widget, simply pass the widget to the
    ToolTip constructor
    """ 
    def __init__(self, widget, message=None, message_fnc=None, delay=1.0, follow=True):
        """
        Initialize the ToolTip
        
        Arguments:
          widget:      The widget this ToolTip is assigned to
          message:     A static string message assigned to the ToolTip
          message_fnc: A function that returns a string to use as the ToolTip text
          delay:       The delay in seconds before the ToolTip appears(may be float)
          follow:      If True, the ToolTip follows motion, otherwise hides
        """
        self.widget = widget
        self.parent = self.widget.master
        Toplevel.__init__(self, self.parent, bg='black', padx=1, pady=1)
        self.withdraw()              # initially hide the tooltip
        self.overrideredirect(True)  # instruct the window manager to NOT decorate this window
        
        self.message = StringVar(value="No tooltip provided" if message is None else message)
        self.message_fnc = message_fnc
        self.delay = delay
        self.follow = follow
        self.visible = False
        self.enter_time = None
        self.label = Label(self, textvariable=self.message, bg="#FFFFDD")
        self.label.grid()
        self.widget.bind("<Enter>", self.spawn, add=True)
        self.widget.bind("<Leave>", self.hide, add=True)
        self.widget.bind("<Motion>", self.move, add=True)
        
    def spawn(self, event=None):
        """
        Spawn the ToolTip.  This simply makes the ToolTip eligible for display.
        Usually this is caused by entering the widget
        
        Arguments:
          event: The event that called this funciton
        """
        self.enter_time = time()
        if self.delay is not None:
            self.after(int(self.delay * 1000), self.show)
            
    def show(self, message=None):
        """
        Displays the ToolTip if the time delay has been long enough
        """
        if self.enter_time is not None and \
        (self.delay is None or time() - self.enter_time >=  self.delay):
            if message is not None:
                self.message.set(message)
            elif self.message_fnc is not None:
                message = self.message_fnc()
                if message is None:
                    self.hide()
                    return
                self.message.set(message)
            self.deiconify()
            self.visible = True
            
    def move(self, event):
        """
        Processes motion within the widget.
        
        Arguments:
          event: The event that called this function
        """
        if not self.follow:
            self.hide()
        self.show()
        if self.enter_time is not None:
            self.geometry("+%d+%d" % (event.x_root + 10, event.y_root + 10))
            
    def hide(self, event=None):
        """
        Hides the ToolTip.  Usually this is caused by leaving the widget
        
        Arguments:
          event: The event that called this function
        """
        self.withdraw()
        self.visible = False
        if event is not None:
            self.enter_time = None
            